#include <iostream>
#include <fstream>
#include <string>
#include <filesystem>
#include <vector>
#include <thread>
#include <regex>
// using namespace std;
#include "curl/curl.h"

#define LOG(message) \
    std::cout << "[INFO] [" << __FILE__ << ":" << __LINE__ << "] " << message << std::endl

struct config
{
    std::string IDM_PATH;  // IDMan.exe的路径
    std::string SAVE_PATH; // 文件的保存路径
};

/**
 * 读取配置文件并解析配置信息
 * @param file_name 配置文件路径
 * @param config 配置信息结构体指针，用于存储读取到的配置数据
 * @return 读取成功返回true，失败返回false
 */
bool read_config(const std::string file_name, config *config)
{
    // 检查文件名是否为空
    if (file_name.empty())
    {
        LOG("The file name cannot be empty!");
        return false;
    }
    std::ifstream config_file(file_name);
    if (!config_file.is_open())
    {
        LOG(file_name + " not found!");
        return false;
    }
    // 从配置文件中读取IDM路径和保存路径
    std::getline(config_file, config->IDM_PATH);
    std::getline(config_file, config->SAVE_PATH);
    config_file.close();
    // 按=分割config->IDM_PATH
    std::string::size_type pos = config->IDM_PATH.find("=");
    config->IDM_PATH = config->IDM_PATH.substr(pos + 1);
    // 判断config->IDM_PATH是否存在
    if (!std::filesystem::exists(config->IDM_PATH)) {
        LOG("IDM_PATH does not exist");
        return false;
    }
    // 按=分割config->SAVE_PATH
    pos = config->SAVE_PATH.find("=");
    config->SAVE_PATH = config->SAVE_PATH.substr(pos + 1);
    // 判断config->SAVE_PATH是否存在
    if (!std::filesystem::exists(config->SAVE_PATH)) {
        LOG("SAVE_PATH does not exist");
        return false;
    }
    return true;
}

/**
 * @brief libcurl的写回调函数，用于接收HTTP响应数据并存储到std::string中
 *
 * @param data 指向接收到的数据块的指针
 * @param size 每个数据单元的大小
 * @param nmemb 数据单元的数量
 * @param clientp 用户自定义数据指针，这里指向std::string对象
 * @return size_t 实际处理的字节数
 */
static size_t writ_fun(char *data, size_t size, size_t nmemb, void *clientp)
{
    // 计算实际接收到的数据大小（字节数）
    size_t realsize = size * nmemb;
    // 将用户数据指针转换为std::string指针
    std::string *response = static_cast<std::string *>(clientp);
    // 将接收到的数据追加到string对象中
    response->append(data, realsize);
    // 返回处理的字节数
    return realsize;
}

/**
 * @brief 通过解析虚假地址获取真实的下载地址
 * @param fake_address 虚假的下载地址
 * @return 真实的下载地址，如果获取失败则返回空字符串
 */
std::string get_real_download_address(const std::string fake_address)
{
    CURL *curl;
    std::string response;
    curl = curl_easy_init();
    if (curl)
    {
        // 设置SSL证书验证选项
        curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); // 忽略SSL证书
        curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L); // 忽略SSL证书

        // 配置curl请求参数
        curl_easy_setopt(curl, CURLOPT_URL, fake_address.c_str());    // 设置URL
        curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&response); // 把存储空间传入回调
        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writ_fun);      // 设置回调函数

        // 执行HTTP请求
        CURLcode res = curl_easy_perform(curl); // 执行
        if (res != CURLE_OK)
        {
            std::string error;
            error.append("curl_easy_perform() failed:");
            error.append(curl_easy_strerror(res));
            LOG(error);
            curl_easy_cleanup(curl); // 释放
            return "";
        }
        curl_easy_cleanup(curl); // 释放

        // 使用正则表达式从响应中提取真实下载地址
        std::regex url_pattern(R"((https://fuckingfast\.co/dl/.*)("))"); // 正则表达式
        std::smatch matches;                                             // 匹配结果
        if (std::regex_search(response, matches, url_pattern))
        {
            return matches[1];
        }
        else
        {
            return "";
        }
    }
    return "";
}

/**
 * @brief 读取文件内容到字符串向量中
 * @param file_name 要读取的文件名
 * @param p_lines 指向存储文件行内容的字符串向量指针
 * @return 读取成功返回true，失败返回false
 */
bool read_file(std::string file_name, std::vector<std::string> *p_lines)
{
    // 检查文件名是否为空
    if (file_name.empty())
    {
        LOG("The file name cannot be empty!");
        return false;
    }
    // 检查文件是否存在
    if (!std::filesystem::exists(file_name))
    {
        LOG(file_name + " file does not exist!");
        return false;
    }
    else
    {
        // 逐行读取文件内容并存储到向量中
        std::ifstream ifs(file_name);
        std::string line;
        while (std::getline(ifs, line))
        {
            // std::cout << line << std::endl;
            if (line.find("http")==std::string::npos) continue;
            p_lines->push_back(line);
        }
        return true;
    }
}

int main(int argc, char const *argv[])
{
    // 读取配置文件
    config cfg;
    if (!read_config("config.cfg", &cfg))
    {
        std::cout << "config.cfg not found!" << std::endl;
        LOG("config.cfg not found!");
        return -1;
    }

    // 由DL_List.txt读取假地址，并遍历
    std::string real_address = "";
    std::vector<std::string> fake_address_s;
    if (read_file("DL_List.txt", &fake_address_s))
    {
        std::string print_str;
        for (std::string fake_address : fake_address_s)
        {
            // 取得真实下载地址
            real_address = get_real_download_address(fake_address);
            if (real_address == "") continue;
            // 取得保存文件名
            std::string::size_type pos = fake_address.find("#");
            std::string save_file_name = fake_address.substr(pos + 1);
            // 判断要下载的文件是否已存在
            if (std::filesystem::exists(cfg.SAVE_PATH + "\\" + save_file_name))
            {
                print_str.append(cfg.SAVE_PATH + "\\" + save_file_name + " download completed.\n");
                std::cout << "\e[1;1H\e[2J" << std::flush; // 清屏命令 
                std::cout << print_str << std::endl;                    
                continue;
            }
            // 拼接CMD命令，向IDM发送下载命令
            std::string cmd;
            cmd.append("start \"\" ");
            cmd.append("\"");
            cmd.append(cfg.IDM_PATH);
            cmd.append("\"");
            cmd.append(" /d \"" + real_address + "\"");
            cmd.append(" /p \"" + cfg.SAVE_PATH + "\"");
            cmd.append(" /f \"" + save_file_name + "\"");
            cmd.append(" /n");
            // std::cout << cmd << std::endl;
            system(cmd.c_str());

            // 循环判断文件是否下载完成
            // std::cout << cfg.SAVE_PATH + "\\" + save_file_name << std::endl;
            int i = 1;
            
            while (true)
            {
                if (std::filesystem::exists(cfg.SAVE_PATH + "\\" + save_file_name))
                {
                    print_str.append(cfg.SAVE_PATH + "\\" + save_file_name + " download completed.\n");
                    std::cout << "\e[1;1H\e[2J" << std::flush; // 清屏命令 
                    std::cout << print_str << std::endl;                    
                    break;
                }
                else
                {
                    std::cout << "\e[1;1H\e[2J" << std::flush; // 清屏命令 
                    std::cout << print_str << std::endl;   
                    std::cout << cfg.SAVE_PATH + "\\" + save_file_name + " download..." << i << std::endl;
                    i++;
                    Sleep(10000);
                }
            }
        }
        std::cout << "download all complete" << std::endl;
        system("pause");
    }
    return 0;
}
